﻿using System;
using Microsoft.AspNetCore.SignalR;
using SimulationCenter.Tasks;
using SimulationCenter.Tasks.Svrs;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using SimulationCenter.Servers;
using Microsoft.Extensions.Logging;

namespace SimulationCenter.Scripts
{
    public class ScriptManager : IDisposable
    {
        private readonly IHubContext<AdbHub> _hub;
        private readonly ConcurrentDictionary<string, ScriptExecutor> _scriptExecutors;
        private readonly ConcurrentDictionary<string, ScriptExecutorBase> _executors;
        private readonly ISubscriptionSvr _subscriptionSvr;
        private readonly ITaskSvr _taskSvr;
        private readonly ServerManager _serverManager;
        private readonly ILogger<ScriptManager> _logger;

        public ScriptManager(IHubContext<AdbHub> hub, ISubscriptionSvr subscriptionSvr, ITaskSvr taskSvr, ServerManager serverManager, ILogger<ScriptManager> logger)
        {
            _hub = hub;
            _subscriptionSvr = subscriptionSvr;
            _taskSvr = taskSvr;
            _serverManager = serverManager;
            _scriptExecutors = new ConcurrentDictionary<string, ScriptExecutor>();
            _executors = new ConcurrentDictionary<string, ScriptExecutorBase>();
            _logger = logger;
        }

        /// <summary>
        /// 指定序列号的终端执行脚本
        /// </summary>
        /// <param name="serial"></param>
        public void Execute(string serial)
        {
            var scriptExecutor = GetOrCreate(serial);

            if (scriptExecutor.Status != ExecutorStatus.Ready) return;

            Task.Factory.StartNew(() =>
            {
                scriptExecutor.Start();
            });
        }

        /// <summary>
        /// 添加检索设备中所有关注的公众号任务
        /// </summary>
        /// <param name="scTaskId"></param>
        /// <param name="device"></param>
        public async Task RunFetchSubscriptionsExecutor(int scTaskId, string device)
        {
            if (_executors.ContainsKey(device))
            {
                if (_executors.TryGetValue(device, out var executor))
                {
                    if (executor.Status == ExecutorStatus.Running) return;
                }

                if (_executors.TryRemove(device, out _))
                {
                    executor?.Quit();
                    executor?.Dispose();
                }
            }

            var fetchSubscriptionsExecutor = new FetchSubscriptionsScriptExecutor(_serverManager.GetOrStart(device), scTaskId, device);

            fetchSubscriptionsExecutor.OnFetchSubscription += Executor_OnFetchSubscription;

            if (_executors.TryAdd(device, fetchSubscriptionsExecutor))
            {
                fetchSubscriptionsExecutor.SafeStart();
                fetchSubscriptionsExecutor.Quit();

                _executors.TryRemove(device, out _);
                fetchSubscriptionsExecutor.Dispose();

                // 启动公众号消息采集任务
                await RunFetchSubscriptionMsgsExecutor(scTaskId, device);
            }
        }

        /// <summary>
        /// 检索设备中的微信号
        /// </summary>
        /// <param name="scTaskId"></param>
        /// <param name="device"></param>
        public async Task RunFetchSubscriptionMsgsExecutor(int scTaskId, string device)
        {
            var scTask = await _taskSvr.GetTask(scTaskId);

            _logger.LogInformation($"设备'{device}'尝试获取下一个任务");

            var deviceTask = await _taskSvr.TryGetNextDeviceTask(scTaskId, device);

            if (deviceTask == null)
            {
                var existsOthers = await _taskSvr.ExistsOtherDeviceTask(scTaskId, device);

                _logger.LogInformation($"设备'{device}' 未检索到可执行任务， 检查当前主任务是否存在其他可执行设备任务 ：{existsOthers}");

                if (scTask.LoopMode == ScLoopMode.Once && !existsOthers)
                {
                    _logger.LogInformation($"任务 {scTask.Id}_{scTask.Name} 执行完毕");

                    scTask.Status = ScTaskStatus.Complete;
                    scTask.EndAt = DateTime.Now;
                    await _taskSvr.UpdateTask(scTask);
                }
                return;
            }

            _logger.LogInformation($"设备'{device}'的下一个任务，检索公众号 '{deviceTask.Subscription}'");

            if (_executors.ContainsKey(device))
            {
                if (_executors.TryGetValue(device, out var executor))
                {
                    if (_executors.TryRemove(device, out _))
                    {
                        executor?.Quit();
                        executor?.Dispose();
                    }
                }
            }

            // 执行任务
            var fetchMsgsExecutor = new FetchSubscriptionMsgsScriptExecutor(_serverManager.GetOrStart(device), _subscriptionSvr, device, deviceTask.Subscription);

            fetchMsgsExecutor.OnStart += async (sender, e) =>
            {
                deviceTask.StartAt = DateTime.Now;
                deviceTask.EndAt = null;
                deviceTask.Status = ScTaskStatus.Running;
                await _taskSvr.UpdateDeviceTask(deviceTask);
            };

            fetchMsgsExecutor.OnComplete += async (sender, e) =>
            {
                deviceTask.Status = ScTaskStatus.Complete;
                await _taskSvr.UpdateDeviceTask(deviceTask);
            };

            fetchMsgsExecutor.OnError += async (sender, e) =>
            {
                fetchMsgsExecutor.Quit();
                fetchMsgsExecutor?.Dispose();

                _logger.LogInformation($"设备序列号'{device}'检索公众号'{deviceTask.Subscription}'失败.");
                await CompleteDeviceTask(deviceTask.Subscription);

                deviceTask.Status = ScTaskStatus.Error;
                await _taskSvr.UpdateDeviceTask(deviceTask);
            };

            fetchMsgsExecutor.OnQuit += async (sender, e) =>
            {
                deviceTask.EndAt = DateTime.Now;
                await _taskSvr.UpdateDeviceTask(deviceTask);

                // _logger.LogInformation($"设备序列号'{device}'开始检索下一个公众号.");
                // await RunFetchSubscriptionMsgsExecutor(scTaskId, device);
            };

            if (_executors.TryAdd(device, fetchMsgsExecutor))
            {
                _logger.LogInformation($"设备'{device}'开始检索公众号 '{deviceTask.Subscription}'");
                await Task.Factory.StartNew(fetchMsgsExecutor.SafeStart);
            }
        }

        /// <summary>
        /// 检索设备中的公众号
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void Executor_OnFetchSubscription(object sender, FetchSubscriptionArgs e)
        {
            await _subscriptionSvr.BindDeviceSubscription(e.Device, e.Subscription);
            await _taskSvr.BindDeviceTask(e.ScTaskId, e.Device, e.Subscription);
        }


        /// <summary>
        /// 公众号已完成检索
        /// </summary>
        /// <param name="subscription"></param>
        public void CompleteSubscription(string subscription)
        {
            var executors = _scriptExecutors.Where(s => subscription.Equals(s.Value?.CurrentSubscription));

            Parallel.ForEach(executors, executor => executor.Value.MoveNextSubscription());
        }

        /// <summary>
        /// 公众号已采集完
        /// </summary>
        /// <param name="subscription"></param>
        /// <returns></returns>
        public Task CompleteDeviceTask(string subscription)
        {
            _logger.LogInformation($"公众号'{subscription}'申请结束任务.");

            return Task.Factory.StartNew(() =>
            {
                var deviceTasks = _taskSvr.GetDeviceTasksBySubscription(subscription);

                _logger.LogInformation($"目前有 {deviceTasks.Count} 个任务在检索公众号'{subscription}'");

                Parallel.ForEach(deviceTasks, async deviceTask =>
                {
                    if (_executors.TryGetValue(deviceTask.Serial, out var executor)
                        && executor is FetchSubscriptionMsgsScriptExecutor msgsScriptExecutor)
                    {
                        _logger.LogInformation($"设备序列号'{deviceTask.Serial}', 公众号'{subscription}' 结束任务.");

                        msgsScriptExecutor?.Quit();
                        msgsScriptExecutor?.Dispose();

                        _logger.LogInformation($"设备序列号'{deviceTask.Serial}'开始检索下一个公众号.");
                        await RunFetchSubscriptionMsgsExecutor(deviceTask.ScTaskId, deviceTask.Serial);
                    }
                });
            });
        }

        #region privates 

        private ScriptExecutor GetOrCreate(string serial)
        {
            var scriptExecutor = _scriptExecutors.GetOrAdd(serial,
                s =>
                {
                    var executor = new ScriptExecutor(s);
                    executor.OnStatusChanged += ScriptExecutor_OnStatusChanged;
                    return executor;
                });

            if (scriptExecutor.Status == ExecutorStatus.Complete ||
                scriptExecutor.Status == ExecutorStatus.Disposed ||
                scriptExecutor.Status == ExecutorStatus.Error)
            {
                _scriptExecutors.TryRemove(serial, out scriptExecutor);
                scriptExecutor?.Dispose();

                scriptExecutor = new ScriptExecutor(serial);
                scriptExecutor.OnStatusChanged += ScriptExecutor_OnStatusChanged;

                _scriptExecutors.TryAdd(serial, scriptExecutor);
            }

            return scriptExecutor;
        }

        private async void ScriptExecutor_OnStatusChanged(object sender, ExecutorStatusChangedArgs args)
        {
            await _hub.Clients.All.SendAsync("executor_status_changed", args);
        }

        #endregion

        public void Dispose()
        {
            _serverManager?.Dispose();
        }
    }
}
